﻿using Microsoft.AspNetCore.Identity;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.Json.Serialization;

namespace net8.Extension
{
    public static class LinqExtension
    {
        /// <summary>
        /// Orders the by.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="direction">The direction.</param>
        /// <returns></returns>
        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, OrderDirection direction)
        {
            switch (direction)
            {
                case OrderDirection.ASC:
                    return source.OrderBy(ToLambda<T>(propertyName));

                case OrderDirection.DESC:
                    return source.OrderByDescending(ToLambda<T>(propertyName));

                default:
                    return source.OrderBy(ToLambda<T>(propertyName));
            }

        }

        /// <summary>
        /// Orders the by.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="direction">The direction.</param>
        /// <returns></returns>
        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, int direction)
        {
            var dir = (OrderDirection)direction;

            switch (dir)
            {
                case OrderDirection.ASC:
                    return source.OrderBy(ToLambda<T>(propertyName));

                case OrderDirection.DESC:
                    return source.OrderByDescending(ToLambda<T>(propertyName));

                default:
                    return source.OrderBy(ToLambda<T>(propertyName));
            }

        }


        /// <summary>
        /// Orders the by.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns></returns>
        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName)
        {
            return source.OrderBy(ToLambda<T>(propertyName));
        }

        /// <summary>
        /// Orders the by descending.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns></returns>
        public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string propertyName)
        {
            return source.OrderByDescending(ToLambda<T>(propertyName));
        }

        public static IQueryable<T> Where<T>(this IQueryable<T> source, string propertyName, string content)
        {
            return source.Where(ToLambdaWhere<T>(propertyName, content));
        }

        public static IQueryable<T> WhereList<T>(this IQueryable<T> source, string propertyName, List<string> contentList)
        {
            return source.Where(ToLambdaWhere<T>(propertyName, contentList));
        }

        /// <summary>
        /// Where Lambda 表示式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <param name="contentList"></param>
        /// <returns></returns>
        private static Expression<Func<T, bool>> ToLambdaWhere<T>(string propertyName, List<string> contentList)
        {
            // 传递到表达式目录树的参数 x 和它的类型
            ParameterExpression x = Expression.Parameter(typeof(T));

            MemberExpression property = Expression.Property(x, propertyName);
            ConstantExpression constant = Expression.Constant(contentList, typeof(List<string>));

            var methodCall = Expression.Call(constant, "Contains", Type.EmptyTypes, property);


            var expression = Expression.Lambda<Func<T, bool>>(methodCall, new ParameterExpression[] { x });
            return expression;
        }

        /// <summary>
        /// Where Lambda 表示式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        private static Expression<Func<T, bool>> ToLambdaWhere<T>(string propertyName, string content)
        {
            // 传递到表达式目录树的参数 x 和它的类型
            ParameterExpression x = Expression.Parameter(typeof(T));
            // 获取类型的属性
            PropertyInfo prop = typeof(T).GetProperty(propertyName);
            // 设定常量值相当于Contains中传递的值
            ConstantExpression constant = Expression.Constant(content, typeof(string));
            // 使用类型调用对应的属性
            var propExp = Expression.Property(x, prop);
            // 获取方法
            MethodInfo contains = typeof(string).GetMethod("Contains", new Type[] { });
            // 给属性调用上面获取的方法，传递值
            var methodCall = Expression.Call(propExp, contains, new Expression[] { constant });
            // 拼装成表达式目录树
            Expression<Func<T, bool>> expression = Expression.Lambda<Func<T, bool>>(methodCall, new ParameterExpression[] { x });
            return expression;
        }

        /// <summary>
        /// Converts to lambda.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns></returns>
        private static Expression<Func<T, object>> ToLambda<T>(string propertyName)
        {
            // 传递到表达式目录树的参数 x
            ParameterExpression x = Expression.Parameter(typeof(T));
            // 通过传递过来的属性字符串获取对应的属性
            MemberExpression property = Expression.Property(x, propertyName);
            // 创建一个表示类型转换运算的 UnaryExpression。
            // 使用 property.Type  会在最后转换时出现错误
            var propAsObject = Expression.Convert(property, typeof(object));

            Expression<Func<T, object>> expression = Expression.Lambda<Func<T, object>>(propAsObject, x);
            return expression;
        }

        /// <summary>
        /// 使用自定linq扩展执行排序，查询，分页功能 item1: 未分页结果，item2：分页后的结果
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="coditionEntity"></param>
        /// <returns></returns>
        //public static Tuple<IQueryable<T>, IQueryable<T>> UseCoditionFind<T>(this IQueryable<T> source, CoditionEntity coditionEntity)
        //{
        //    var query = source;
        //    var resultQuery = source;
        //    if (coditionEntity.SearchKeyValue != null && coditionEntity.SearchKeyValue.Enable && !coditionEntity.SearchKeyValue.SearchContent.IsNullOrWhiteSpace())
        //    {
        //        query = query.Where(coditionEntity.SearchKeyValue.PropertyName, coditionEntity.SearchKeyValue.SearchContent);
        //    }
        //    if (coditionEntity.OrderByKeyValue != null && coditionEntity.OrderByKeyValue.Enable)
        //    {
        //        query = query.OrderBy(coditionEntity.OrderByKeyValue.PropertyName, coditionEntity.OrderByKeyValue.OrderDirection);
        //    }
        //    if (coditionEntity.PaginationKeyValue != null && coditionEntity.PaginationKeyValue.Enable)
        //    {
        //        resultQuery = query.Skip((coditionEntity.PaginationKeyValue.PageNumber - 1) * coditionEntity.PaginationKeyValue.PageSize)
        //            .Take(coditionEntity.PaginationKeyValue.PageSize);
        //    }
        //    return new Tuple<IQueryable<T>, IQueryable<T>>(query, resultQuery);
        //}

        //public static Expression<Func<T, bool>> ParserConditions<T>(IEnumerable<QueryCondition> conditions)
        //{
        //    //将条件转化成表达是的Body
        //    var query = ParseExpressionBody(conditions);
        //    return Expression.Lambda<Func<T, bool>>(query, parameter);
        //}

        private static Expression ParseExpressionBody(IEnumerable<QueryCondition> conditions)
        {
            if (conditions == null || conditions.Count() == 0)
            {
                return Expression.Constant(true, typeof(bool));
            }
            else if (conditions.Count() == 1)
            {
                return ParseCondition(conditions.First());
            }
            else
            {
                Expression left = ParseCondition(conditions.First());
                Expression right = ParseExpressionBody(conditions.Skip(1));
                return Expression.AndAlso(left, right);
            }
        }

        private static Expression ParseCondition(QueryCondition condition)
        {
            ParameterExpression p = Expression.Parameter(typeof(int),"a");//parameter;
            Expression key = Expression.Property(p, condition.Key);
            Expression value = Expression.Constant(condition.Value);
            switch (condition.Operator)
            {
                case QueryEnum.Contains:
                    return Expression.Call(key, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), value);
                case QueryEnum.Equal:
                    return Expression.Equal(key, Expression.Convert(value, key.Type));
                case QueryEnum.Greater:
                    return Expression.GreaterThan(key, Expression.Convert(value, key.Type));
                case QueryEnum.GreaterEqual:
                    return Expression.GreaterThanOrEqual(key, Expression.Convert(value, key.Type));
                case QueryEnum.Less:
                    return Expression.LessThan(key, Expression.Convert(value, key.Type));
                case QueryEnum.LessEqual:
                    return Expression.LessThanOrEqual(key, Expression.Convert(value, key.Type));
                case QueryEnum.NotEqual:
                    return Expression.NotEqual(key, Expression.Convert(value, key.Type));
                case QueryEnum.In:
                    return ParaseIn(p, condition);
                case QueryEnum.Between:
                    return ParaseBetween(p, condition);
                default:
                    throw new NotImplementedException("不支持此操作");
            }
        }

        private static Expression ParaseBetween(ParameterExpression parameter, QueryCondition conditions)
        {
            ParameterExpression p = parameter;
            Expression key = Expression.Property(p, conditions.Key);
            var valueArr = conditions.Value.Split(',');
            if (valueArr.Length != 2)
            {
                throw new NotImplementedException("ParaseBetween参数错误");
            }
            try
            {
                int.Parse(valueArr[0]);
                int.Parse(valueArr[1]);
            }
            catch
            {
                throw new NotImplementedException("ParaseBetween参数只能为数字");
            }
            Expression expression = Expression.Constant(true, typeof(bool));
            //开始位置
            Expression startvalue = Expression.Constant(int.Parse(valueArr[0]));
            Expression start = Expression.GreaterThanOrEqual(key, Expression.Convert(startvalue, key.Type));

            Expression endvalue = Expression.Constant(int.Parse(valueArr[1]));
            Expression end = Expression.GreaterThanOrEqual(key, Expression.Convert(endvalue, key.Type));
            return Expression.AndAlso(start, end);
        }

        private static Expression ParaseIn(ParameterExpression parameter, QueryCondition conditions)
        {
            ParameterExpression p = parameter;
            Expression key = Expression.Property(p, conditions.Key);
            var valueArr = conditions.Value.Split(',');
            Expression expression = Expression.Constant(true, typeof(bool));
            foreach (var itemVal in valueArr)
            {
                Expression value = Expression.Constant(itemVal);
                Expression right = Expression.Equal(key, Expression.Convert(value, key.Type));

                expression = Expression.Or(expression, right);
            }
            return expression;
        }

        //public static IQueryable<T> QueryConditions<T>(this IQueryable<T> query, IEnumerable<QueryCondition> conditions)
        //{
        //    var parser = new UosoExpressionParser<T>();
        //    var filter = parser.ParserConditions(conditions);
        //    return query.Where(filter);
        //}

        public static IQueryable<T> OrderConditions<T>(this IQueryable<T> query, IEnumerable<OrderCondition> orderConditions)
        {
            foreach (var orderinfo in orderConditions)
            {
                var t = typeof(T);
                var propertyInfo = t.GetProperty(orderinfo.Key);
                var parameter = Expression.Parameter(t);
                Expression propertySelector = Expression.Property(parameter, propertyInfo);

                var orderby = Expression.Lambda<Func<T, object>>(propertySelector, parameter);
                if (orderinfo.Order == OrderEnum.DESC)
                    query = query.OrderByDescending(orderby);
                else
                    query = query.OrderBy(orderby);

            }
            return query;
        }

        public static IQueryable<T> Pager<T>(this IQueryable<T> query, int pageindex, int pagesize, out int itemCount)
        {
            itemCount = query.Count();
            return query.Skip((pageindex - 1) * pagesize).Take(pagesize);
        }

        //public IList<IdentityUser> GetPagedList2(IEnumerable<QueryCondition> conditions, IEnumerable<OrderCondition> orderConditions, int pageIndex, int pageSize, out int itemcount)
        //{
        //    return _userManager.Users.AsNoTracking().QueryConditions(conditions).OrderConditions(orderConditions).Pager(pageIndex, pageSize, out itemcount).ToList();
        //}

        //List<UosoConditions> uosoConditions = new List<UosoConditions>() {
        //         new UosoConditions { Key = "UserName", Operator = UosoOperatorEnum.Contains, Value = "1,3", ValueType = "string" }
        //    };
        //List<UosoOrderConditions> orderConditions = new List<UosoOrderConditions> {
        //         new UosoOrderConditions{
        //              Key="UserName",
        //               Order = OrderSequence.DESC
        //         },
        //         new UosoOrderConditions{
        //              Key="PhoneNumber",
        //               Order = OrderSequence.DESC
        //         }
        //    };
    }

    /// <summary>
    /// 查询基本实体
    /// </summary>
    public class CoditionEntity
    {
        [JsonPropertyName("paginationKeyValue")]
        public PaginationKeyValue PaginationKeyValue { get; set; }

        [JsonPropertyName("orderByKeyValue")]
        public OrderByKeyValue OrderByKeyValue { get; set; }

        [JsonPropertyName("searchKeyValue")]
        public SearchKeyValue SearchKeyValue { get; set; }
    }
    /// <summary>
    /// 关键字搜索
    /// </summary>
    public class SearchKeyValue
    {
        private string _propertyName;
        [JsonPropertyName("enable")]
        public bool Enable { get; set; }
        [JsonPropertyName("propertyName")]
        public string PropertyName
        {
            get
            {
                // 因为前端传递的是小写字母开头的，但是在后端都是大写字母开头的，我们需要在获取时将首字母大写
                return _propertyName;//.ToUpperFirstLetter();
            }
            set
            {
                _propertyName = value;
            }
        }
        [JsonPropertyName("searchContent")]
        public string SearchContent { get; set; }
    }

    /// <summary>
    /// 排序字段格式
    /// </summary>
    public class OrderByKeyValue
    {
        private string _propertyName;
        [JsonPropertyName("enable")]
        public bool Enable { get; set; }
        [JsonPropertyName("propertyName")]
        public string PropertyName
        {
            get
            {
                return _propertyName;//.ToUpperFirstLetter();
            }
            set
            {
                _propertyName = value;
            }
        }
        [JsonPropertyName("orderDirection")]
        public OrderDirection OrderDirection { get; set; }
    }
    /// <summary>
    /// 分页格式
    /// </summary>
    public class PaginationKeyValue
    {
        [JsonPropertyName("enable")]
        public bool Enable { get; set; }
        [JsonPropertyName("count")]
        public int Count { get; set; }
        [JsonPropertyName("pageSize")]
        public int PageSize { get; set; }
        [JsonPropertyName("pageNumber")]
        public int PageNumber { get; set; }
    }

    /// <summary>
    /// 排序类型
    /// </summary>
    public enum OrderDirection
    {
        /// <summary>
        /// 升序
        /// </summary>
        [Description("升序")]
        ASC = 0,
        /// <summary>
        /// 降序
        /// </summary>
        [Description("降序")]
        DESC = 1
    }

    public class QueryCondition
    {
        /// <summary>
        /// 字段名称
        /// </summary>
        public string Key { get; set; }
        /// <summary>
        /// 值
        /// </summary>
        public string Value { get; set; }
        /// <summary>
        /// 值类型
        /// </summary>
        public string ValueType { get; set; }
        /// <summary>
        /// 查询条件
        /// </summary>
        public QueryEnum Operator { get; set; }
    }

    public enum QueryEnum
    {
        Contains,
        Equal,
        Greater,
        GreaterEqual,
        Less,
        LessEqual,
        NotEqual,
        In,
        Between
    }

    public class OrderCondition
    {
        /// <summary>
        /// 字段名称
        /// </summary>
        public string Key { get; set; }
        /// <summary>
        /// 排序条件
        /// </summary>
        public OrderEnum Order { get; set; }
    }
    public enum OrderEnum
    {
        ASC,
        DESC
    }
}
